/**
 * main class (entry point) of extension.
 *
 * handles xul related user interface stuff and functionalities bind
 * to these stuff (both controller and view in MCV approach)
 *
 */
var imkbizle = function() {
	/* bof private area */

	/**
	 * @private variable
	 * Integer, last interval value for periodical call of
	 * imkbizle.refreshInformation function.
	 */
	var last_interval;

	/**
	 * @private variable
	 * Integer, last interval id returned by setInterval function call
	 * to set period of autorefreshs.
	 */
	var last_interval_id;

	/**
	 * @private function
	 * sets label of given button to "<STOCK_NAME>: <STOCK_VALUE>"
	 * format.
	 *
	 * @param Hash stock, that stores information about a stock
	 * retrieved from source site.
	 * @param XULElement button, whose label will be set.
	 *
	 * @return void
	 */
	var setLabel = function(stock, button) {
		button.label = stock.Name + ": " + stock.Latest;
	};

	/**
	 * @private function
	 * sets stock button's image to an upper or lower arrow image
	 * according to stock's latest-open values comparision.
	 *
	 * @param Hash stock, that stores information about a stock
	 * retrieved from source site.
	 * @param XULElement button, whose "image" attribute will be set.
	 *
	 * @return void
	 */
	var setArrow = function(stock, button) {
		button.image = "chrome://imkbizle/skin/arrow_"
			+ ((stock.Latest >= stock.Open) ? "up" : "down") + ".gif";
	};

	/**
	 * @private function
	 * adds all the information of stock to tooltip separated by a
	 * pipe(|) sign, excluding name of stock. format is so:
	 * "<HASH_KEY1>: <HASH_VALUE1> | <HASH_KEY2>: <HASH_VALUE2> ..."
	 * (HASH represents the hash passed by parameter that stores stock information)
	 *
	 * @param Hash stock, that stores information about a stock
	 * retrieved from source site.
	 * @param XULElement button, whose "tooltipText" attribute will
	 * be set.
	 *
	 * @return void
	 */
	var setToolTipText = function(stock, button) {
		var tiptext = "";
		var stats = $H(stock);
		stats.unset("Name"); //exclude info that won't be shown on tooltip
		stats.each(function(stat) {
			if (stat.value) {
				tiptext += imkbizle.locale.getString("stock." + stat.key) + ": " + stat.value + " | ";
			}
		});
		button.tooltipText = tiptext.replace(/(.*) \| $/, "$1"); //strip last seperator
	};

	/**
	 * @private function
	 * response handler callback for a regular request to retrieve
	 * statistics of chosen stock from source site. parses html
	 * response, gets stock information, and fills stock button label
	 * and tooltip according to this data, sets arrow direction
	 * according to stock moves. target stock button info comes from
	 * passed parameter to this callback via prototype library's bind
	 * function.
	 *
	 * @param XMLHttpRequest object transport, that stores response
	 * data.
	 *
	 * @return void
	 */
	var infoReceived = function(transport) {
		var stock = prefs.get_source_site().getStock(transport.responseText);
		var target_button = this; //see bind method of prototype library.
		setLabel(stock, target_button);
		setArrow(stock, target_button);
		setToolTipText(stock, target_button);
	};

	/**
	 * @private function
	 * response handler callback for a non-regular request to retrieve
	 * extra statistics from a source site (such as exchange
	 * values). if stock select button contains a non-stock item, this
	 * callback will be used for these extra items. source_site class
	 * must have a function with name "getExtra" in this case. the key
	 * name for selected extra item will be first param, and the
	 * response text returned from request will be second param for
	 * this function call. it must return a hash with "value" and
	 * "tooltip" keys.  label of given button will be set to
	 * "<EXTRA_KEY>: <EXTRA_VALUE>" format, and tooltip attribute of
	 * returned object from getExtra function will be used for button
	 * tooltip.
	 *
	 * @param String extrakey, key name of the extra item (ex: USDTRY
	 * for usd-turkish lira exchange values.)
	 * @param XMLHttpRequest object transport, that stores response
	 * data.
	 *
	 * @return void
	 */
	var extraInfoReceived = function(extrakey, transport) {
		var extra = prefs.get_source_site().getExtra(extrakey, transport.responseText);
		var target_button = this; //see bind method of prototype library.
		target_button.label = extrakey + ": " + extra.value;
		target_button.tooltipText = extra.tooltip;
	};

	/**
	 * @private function
	 * resets button to loading state. sets image to empty string,
	 * sets label and tooltip text to a "loading" text in current
	 * locale.
	 *
	 * @param XULElement button, which will be reset.
	 *
	 * @return void
	 */
	var resetButton = function(button) {
		button.label = imkbizle.locale.getString("status.loading");
		button.image = "";
		button.tooltipText = imkbizle.locale.getString("status.wait");
	};

	/**
	 * @private function
	 * adds a menuitem element to given menu, with given label and
	 * callback.
	 *
	 * @param XUL:menupopup menu, dom of a menupopup.  @param String
	 * label, which will be used as new item's label.  @param Function
	 * click_callback, which will be used as onclick callback for new
	 * item (this is not a mandatory param).
	 *
	 * @return void
	 */
	var addMenuItem = function(menu, label, click_callback) {
		var item = document.createElement("menuitem");
		item.setAttribute("label", label);
		if (click_callback) {
			item.setAttribute("onclick", click_callback);
		}
		menu.appendChild(item);
	};

	/**
	 * @private function
	 * fills stock select button with the list retrieved from
	 * stocks.properties file. adds extra items defined for selected
	 * source site to the end.
	 *
	 * @return array, stock list (without extra items).
	 */
	var fillStockList = function() {
		var stocks = prefs.getStocks();
		stocks.each(function(stock) {
			addMenuItem($('stock-menu'), stock);
		});
		prefs.get_source_site().extras.each(function(extra) {
			addMenuItem($('stock-menu'), extra.key);
		});
		// FIXME: this method is called only once at startup, if user
		// changes source site afterwards, the extra item list will not
		// be changed. it's not a problem for now, because both akbank
		// and isnet sources use same extra item list.
		return stocks;
	};
	/* eof private area */

	return {
	/* bof public area */

		/**
		 * @public variable
		 * Boolean, flag to control refresh of stock statistics.
		 */
		refresh_enabled: true,

		/**
		 * @public variable
		 * XUL:stringbundle, stores localization strings used in
		 * javascript.
		 */
		locale: null,

		/**
		 * @public function
		 * sets stock selection to saved default stock, does first
		 * request to get statistics of that stock, sets an interval
		 * handler to repeat requests for refreshing
		 * information. interval time comes from preferences.
		 *
		 * @return void
		 */
		startup: function() {
			var stocks = fillStockList();
			// check if any saved value exists for default stock to be
			// shown at startup, if none show first stock.
			var index = prefs.getIntPref(prefs.settings.default_stock.target, 0);
			imkbizle.locale = $("string-bundle");
			$("stock-button0").setAttribute("stockname", stocks[index]);
			imkbizle.refreshInformation();
			var refresh_interval = last_interval = prefs.getIntPref(prefs.settings.refresh_interval.target, 2) + 1;
			last_interval_id = window.setInterval("imkbizle.refreshInformation()", refresh_interval*60*1000); //default 3 min.
		},

		/**
		 * @public function
		 * does a request to retrieve information of selected
		 * stock. this function is called periodically to refresh
		 * information
		 *
		 * @param XULElement target_button, in which retrieved stock
		 * info will be showed.
		 *
		 * @return void
		 */
		refreshInformation: function(target_button) {
			if (imkbizle.refresh_enabled) {
				if (!target_button && last_interval_id) { //if a periodical call and not first call
					var refresh_interval = prefs.getIntPref(prefs.settings.refresh_interval.target, 2) + 1;
					if (refresh_interval != last_interval) { //if it's changed by a preferences save
						last_interval = refresh_interval;
						clearInterval(last_interval_id);
						last_interval_id = window.setInterval("imkbizle.refreshInformation()", refresh_interval*60*1000); //default 3 min.
					}
				}
				var source = prefs.get_source_site();
				var query = "button[@class=stock-button]";
				query += target_button ? "[@stockname=" + target_button.getAttribute("stockname") + "]" : "";
				jQuery(query).each(function() {
					var target = this;
					var stockname = target.getAttribute("stockname");
					resetButton(target);
					var request;
					if (!source.extras.keys().include(stockname)) { // if a regular stock
						request = new Ajax.Request(source.url, {
							method: 'get',
							parameters: source.createParams(stockname),
							onSuccess: bind(target, infoReceived),
							onFailure: function(){ throw("couldn't retrieve data from '" + source.url + "'") }
						});
					} else { // if a current source site specific extra (such as exchange values)
						var url = source.extras.get(stockname).url;
						request = new Ajax.Request(url, {
							method: 'get',
							onSuccess: bind(target, extraInfoReceived, stockname),
							onFailure: function(){ throw("couldn't retrieve data from '" + url + "'") }
						});
					}
					target.setAttribute("url", request.url);
				});
			}
		},

		/**
		 * @public function
		 * does a request to retrieve information of selected
		 * stock. this function is called whenever a stock is chosen.
		 *
		 * @param XUL Event event, triggered after selection of stock
		 * on select button list is complete.
		 *
		 * @return void
		 */
		onSelectStock: function(event) {
			var target_button = event.target.parentNode.parentNode;
			target_button.setAttribute("stockname", event.target.label);
			imkbizle.refreshInformation(target_button);
		},

		/**
		 * @public function
		 * shows source page from which the stock information is
		 * retrieved. link target is determined by preferences.
		 *
		 * @param XUL Event event, triggered by clicking "show source
		 * page" link on right click menu.
		 *
		 * @return void
		 */
		showSourcePage: function(event) {
			var load_location = prefs.settings.load_location;
			var current_button = $(event.target.getAttribute("buttonid"));
			var url = current_button.getAttribute("url");
			var target = prefs.getIntPref(load_location.target);
			if (target == prefs.NEW_WINDOW) {
				window.open(url);
			} else if ((target == prefs.NEW_TAB) || (target == prefs.UNFOCUSED_TAB)) {
				var myTab = getBrowser().addTab(url, null, null);
				if(target == prefs.NEW_TAB)
					getBrowser().selectedTab = myTab;
			} else {
				getBrowser().loadURI(url);
			}
		},

		/**
		 * @public function
		 * toggle's periodical refresh functionality. also toggles
		 * rightclick menu item label according to current status.
		 *
		 * @param XUL Event event, triggered by clicking "enable-disable
		 * refresh" menu item on right click menu.
		 *
		 * @return void
		 */
		toggleRefresh: function(event) {
			imkbizle.refresh_enabled = !imkbizle.refresh_enabled;
			var label_key = imkbizle.refresh_enabled
				? "label.disable_refresh"
				: "label.enable_refresh";
			event.target.label = imkbizle.locale.getString(label_key);
			jQuery("button[@class=stock-button]").each(function() {
				this.disabled = !imkbizle.refresh_enabled;
			});
		},

		/**
		 * @public function
		 * open preferences pane (prefs.xul) in a new window.
		 *
		 * @param XUL Event event, triggered by clicking "Preferences"
		 * menu item on right click menu.
		 *
		 * @return void
		 */
		openPreferences: function(event) {
			window.openDialog("chrome://imkbizle/content/prefs.xul",
					"_blank", "centerscreen, chrome,resizable=no,dependent=yes")
		},

		/**
		 * @public function
		 * adds a new stock button to the toolbar. there are two
		 * differences between initial and added stock
		 * button. initial stock button has an "enable-disable
		 * refresh" menu item in it's rightclik menu which added stock
		 * button doesn't have. added stock button has an "Remove"
		 * menu item which initial stock button doesn't have.
		 *
		 * @param XUL Event event, triggered by clicking plus sign near
		 * last stock button in toolbar.
		 *
		 * @return void
		 */
		addStockButton: function(event) {
			var first_id = jQuery("div:first")[0].id;
			var first_id_num = parseInt(first_id.replace(/stock-div(\d+)/, "$1"));
			var new_id_num = first_id_num + 1;
			var new_id = "stock-div" + new_id_num;
			var first_div = $(first_id);
			var new_div = first_div.cloneNode(true);
			var rightclick_menu = new_div.firstChild;
			var show_source_menu = rightclick_menu.firstChild;
			var button = rightclick_menu.nextSibling;
			var menupopup = button.firstChild;
			var remove_callback = "imkbizle.removeStockButton('" + new_id + "')";
			new_div.id = new_id;
			rightclick_menu.id = "rightclick_menu" + new_id_num;
			if (first_id == "stock-div0") {
				// remove disable-enable refresh menu item from non-initial stock buttons.
				var item  = rightclick_menu.firstChild.nextSibling;
				item.parentNode.removeChild(item);
				// add "remove" menu item to non-initial stock buttons.
				addMenuItem(rightclick_menu, imkbizle.locale.getString("label.remove"), remove_callback);
			} else {
				rightclick_menu.lastChild.setAttribute("onclick", remove_callback);
			}
			button.id = "stock-button" + new_id_num;
			show_source_menu.setAttribute("buttonid", button.id);
			button.setAttribute("context", "rightclick_menu" + new_id_num);
			menupopup.id = "stock-menu" + new_id_num;
			event.target.parentNode.insertBefore(new_div, first_div);
		},

		/**
		 * @public function
		 * removes stock button whose rightclick menu item is clicked.
		 *
		 * @param XUL Event event, triggered by clicking "Remove" menu
		 * item on right click menu.
		 *
		 * @return void
		 */
		removeStockButton: function(button_id) {
			button = $(button_id);
			button.parentNode.removeChild(button);
		}
	/* eof public area */
	}
}()

// Install load handler
window.addEventListener("load", function(e) { imkbizle.startup(); }, false);
